﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace Apewer.Web
{

    /// <summary></summary>
    public sealed class ApiFunction : IToJson
    {

        #region fields

        ApiApplication _application = null;
        MethodInfo _method = null;
        Type _return = null;
        ApiParameter[] _parameters = null;

        string _name = null;
        string _lower = null;
        string _caption = null;
        string _description = null;

        bool _hidden = false;

        #endregion

        #region properties

        /// <summary></summary>
        public ApiApplication Application { get => _application; }

        /// <summary>方法。</summary>
        public MethodInfo Method { get => _method; }

        /// <summary>参数。</summary>
        public ApiParameter[] Parameters { get => _parameters.Map(x => x); }

        /// <summary>返回的类型。</summary>
        public Type ReturnType { get => _return; }

        /// <summary></summary>
        public string Name { get => _name; }

        /// <summary></summary>
        public string Caption { get => _caption; }

        /// <summary></summary>
        public string Description { get => _description; }

        /// <summary></summary>
        public bool Hidden { get => _hidden; }

        #endregion

        /// <summary></summary>
        public Json ToJson() => ToJson(true);

        /// <summary></summary>
        public Json ToJson(ApiOptions options) => ToJson((options ?? new ApiOptions()).WithParameters);

        /// <summary></summary>
        public Json ToJson(bool withParameters)
        {
            if (Hidden) return null;

            var json = Json.NewObject();
            json.SetProperty("name", Name);
            if (!string.IsNullOrEmpty(Caption)) json.SetProperty("caption", Caption);
            if (!string.IsNullOrEmpty(Description)) json.SetProperty("description", Description);
            if (withParameters) json.SetProperty("parameters", Json.From(_parameters));
            return json;
        }

        ApiFunction(MethodInfo method, ApiApplication application, ApiParameter[] parameters)
        {
            // 检查 ApiAttribute 特性。
            var apis = method.GetCustomAttributes(typeof(ApiAttribute), false);
            var api = apis.Length > 0 ? (ApiAttribute)apis[0] : null;

            // Entry
            _application = application;
            _method = method;

            // 返回值。
            _return = method.ReturnType;
            if (_return.Equals(typeof(void))) _return = null;

            // api
            if (api == null)
            {
                _name = method?.Name;
                _lower = _name?.Lower();
            }
            else
            {
                _name = string.IsNullOrEmpty(api.Name) ? method?.Name : api.Name;
                _lower = _name?.Lower();
                _caption = api.Caption;
                _description = api.Description;
            }

            // caption
            if (string.IsNullOrEmpty(_caption))
            {
                var captions = method.GetCustomAttributes(typeof(CaptionAttribute), true);
                if (captions.Length > 0)
                {
                    var caption = (CaptionAttribute)captions[0];
                    _caption = caption.Title;
                    if (string.IsNullOrEmpty(_description))
                    {
                        _description = caption.Description;
                    }
                }
            }

            // hidden
            if (method.Contains<HiddenAttribute>(false)) _hidden = true;

            // 参数。
            _parameters = parameters;
        }

        /// <summary></summary>
        public static ApiFunction Parse(ApiApplication application, MethodInfo method)
        {
            if (application == null) return null;
            if (method == null) return null;

            // 滤除构造函数、抽象方法、泛型和非本类定义方法。
            if (method.IsConstructor) return null;
            if (method.IsAbstract) return null;
            if (method.GetGenericArguments().NotEmpty()) return null;

            // 滤除 get 和 set 访问器。
            var methodName = method.Name;
            if (methodName.StartsWith("get_") || methodName.StartsWith("set_")) return null;
            switch (methodName)
            {
                case "Dispose":
                    return null;
            }

            // 定义者。
            var declaring = method.DeclaringType;
            if (declaring.Equals(typeof(object))) return null;

            // 参数，所有参数必须是 In 方向。
            var ps = new List<ApiParameter>();
            foreach (var pi in method.GetParameters())
            {
                var p = ApiParameter.Parse(pi);
                if (p == null) return null;
                ps.Add(p);
            }

            return new ApiFunction(method, application, ps.ToArray());
        }

    }

}
